#include "mf_fat32.h"
#include "spirom.h"
#include "data_types.h"

extern unsigned char recvbuf[512 + 8];

int FAT_Begin(MF_FAT32_Filesystem *fs)
/*************************************************
 *
 * WARNING: It is assumed that rootContents is an array
 *			of 40 elements.
 *
 *************************************************/
{
	int i;
	struct BS_Structure *bpb; //mapping the buffer onto the structure
	MF_FileInfo root;
	
	//INIT Filesystem Stucture
	
	fs->currDirectory.clusterOffset = 0;
	
	for(i = 0; i < CONTENTS_LENGTH; i++)
		fs->dirContents[i].clusterOffset = 0;
		
	for(i = 0; i < DIR_STACK_MAX; i++)
		fs->dirStack[i].clusterOffset = 0;
	
	
	fs->currStackNo = -1;
	
	//Get FAT32 Info
	i = GetVolumeIDData();
	
	if(i == NULL)
		return 1;
	
	bpb = (struct BS_Structure *)recvbuf;

	fs->filesystemInfo.bytesPerSector		= combineInformation(bpb->bytesPerSector, 2);
	fs->filesystemInfo.sectorPerCluster		= bpb->sectorPerCluster;
	fs->filesystemInfo.reservedSectorCount	= combineInformation(bpb->reservedSectorCount, 2);
	fs->filesystemInfo.rootCluster			= combineInformation(bpb->rootCluster, 4);// + (sector / sectorPerCluster) +1;
	fs->filesystemInfo.firstDataSector		= combineInformation(bpb->hiddenSectors, 4) + combineInformation(bpb->reservedSectorCount, 2) + (bpb->numberofFATs * combineInformation(bpb->FATsize_F32, 4));
	fs->filesystemInfo.FAT_Begin_Cluster	= combineInformation(bpb->hiddenSectors, 4) + combineInformation(bpb->reservedSectorCount, 2);
	
	//sd_read_single_block(fs->filesystemInfo.firstDataSector);
	
	//Set Up Root
	root.name[0] = 'R';
	root.name[1] = 'o';
	root.name[2] = 'o';
	root.name[3] = 't';
	root.name[4] = 0;
	
	root.parentDir = NULL;
	root.directoryLevel = 0;
	root.clusterOffset = 0;
	root.isFolder = 1;
	root.isFile = 0;
	
	//Read Root and store in FS
	changeContext(fs, &root);  //Store Root Info
	
	return 0;
}

unsigned long GetVolumeIDData()
{
	struct BS_Structure *bpb;
	struct MBRinfo_Structure *mbr;
	struct partitionInfo_Structure *partition;
	
	unsigned long volumeID_addr;
	
	sd_read_single_block(0);
	
	bpb = (struct BS_Structure *)recvbuf;

	if(bpb->jumpBoot[0]!=0xE9 && bpb->jumpBoot[0]!=0xEB) //check if it is boot sector
	{
	  mbr = (struct MBRinfo_Structure *) recvbuf;         //if it is not boot sector, it must be MBR
	 
	  if(mbr->signature1 != 0x55 || mbr->signature2 != 0xAA) return NULL;            //if it is not even MBR then it's not FAT32
     
 	 partition = (struct partitionInfo_Structure *)(mbr->partitionData);//first partition
 	 volumeID_addr = combineInformation(partition->firstSector, 4); //the unused sectors, hidden to the FAT
 	
 	
 	 sd_read_single_block(volumeID_addr);//read the bpb sector
	 bpb = (struct BS_Structure *)recvbuf;
 	 if(bpb->jumpBoot[0]!=0xE9 && bpb->jumpBoot[0]!=0xEB)  return NULL; 
	}
	
	return volumeID_addr;
}

int readDirectoryInfo(MF_FAT32_Filesystem *fs, unsigned long depth)
{
	int done;
	int i;
	int j, k;
	unsigned long ClusterNumber;
	unsigned long prevClusterSector;
	unsigned long clusterSector;
	unsigned long nextClusterSector;
	
	struct dir_Structure *dir[16];
	struct dir_Long_Structure *longDir[16];
	
	const char blankName[7] = "-None-\0";
	
	for(i = 0; i < 18; i++) 
	{
		fs->dirContents[i].used = 0;
		fs->dirContents[i].isFile = 0;
		fs->dirContents[i].isFolder = 0;
	}
	
	//Read FAT Tables to find Cluster in Data Partition
	
	done = 0;
	i = 0;
	
	ClusterNumber = fs->currDirectory.clusterOffset;
	
	prevClusterSector = findCluster(fs, ClusterNumber, depth - 1); 
	clusterSector = findCluster(fs, ClusterNumber, depth);
	nextClusterSector = findCluster(fs, ClusterNumber, depth + 1);
	
	/*for(i = 0; i < depth; i++)
	{
		OffsetInFATTable = ClusterNumber % 64;
		FATClusterOffset = ClusterNumber / 64;
		FATCluster = fs->filesystemInfo.FAT_Begin_Cluster + FATClusterOffset;
	
		if(PrevFATCluster != FATCluster)
			sd_read_single_block(FATCluster);
		
		PrevFATCluster = FATCluster;
		PrevClusterNumber = ClusterNumber;
		
		//Get Next File Location
		ClusterNumber = combineInformation((unsigned char*)&recvbuf[OffsetInFATTable], 8);
	}
	
		//CHECK FOR EOD
		OffsetInFATTable = ClusterNumber % 64;
		FATClusterOffset = ClusterNumber / 64;
		FATCluster = fs->filesystemInfo.FAT_Begin_Cluster + FATClusterOffset;
	
		if(PrevFATCluster != FATCluster)
			sd_read_single_block(FATCluster);
		*/
		
		
		//0xFFFFFFFF if end of directory
		fs->dirContents[CONTENTS_LENGTH - 1].clusterOffset = nextClusterSector;
		fs->dirContents[CONTENTS_LENGTH - 1].used = 1;
		if(nextClusterSector >= 0x0FFFFFF8)
			fs->dirContents[CONTENTS_LENGTH - 1].used = 0;
			
		fs->dirContents[0].clusterOffset = prevClusterSector;
		fs->dirContents[0].used = 1;
		if(prevClusterSector >= 0x0FFFFFF8)
			fs->dirContents[0].used = 0;
	
	//Location of file found, now pull out data
	sd_read_single_block(fs->filesystemInfo.firstDataSector + clusterSector);
	
	for(i = 0; i < 16; i++)
	{
		dir[i] = (struct dir_Structure *) &recvbuf[32*i];
		longDir[i] = (struct dir_Long_Structure *) &recvbuf[32*i];
	}
	
	i = 0;
	done = 0;
	fs->numContents = 0;
	
	while((!done) && (i < MAX_FILES_PER_READ))
	{
			//Grab Info, till end
			if(dir[i]->attrib == 0x00)
			{
				done = 1;
			}
			else if(dir[i]->attrib != 0x0F) //Short filename
			{
				if(dir[i]->name[0] != 0xE5) // DELETED ENTRY == 0xE5
				{
					//NAME
					strncpy(fs->dirContents[fs->numContents + 1].name, (const char*)dir[i]->name, 11);
					fs->dirContents[fs->numContents + 1].name[11] = '\0';
				
					//DIRECTORY LEVEL
					fs->dirContents[fs->numContents + 1].directoryLevel = fs->currDirectory.directoryLevel;
				
					//clusterOffset
					fs->dirContents[fs->numContents + 1].clusterOffset = (combineInformation(dir[i]->firstClusterHI, 2) << 16) | combineInformation(dir[i]->firstClusterLO, 2) - fs->filesystemInfo.rootCluster;
				
					//IS USED
					fs->dirContents[fs->numContents + 1].used = 1;
					
					//PARENT DIRECTORY
					fs->dirContents[fs->numContents + 1].parentDir = &(fs->currDirectory);
				
					if( (dir[i]->attrib == 0x00) || (dir[i]->attrib == 0x01) )
					{
						fs->dirContents[fs->numContents + 1].isFile = 1;
					}
					else if(dir[i]->attrib == 0x10)
					{
						fs->dirContents[fs->numContents + 1].isFolder = 1;
					}
					
					//UPDATE AMOUNT OF FILES STORED
					fs->numContents++;
				}
				
			}
			else
			{
				//DEAL WITH LONG FILENAMES HERE
			}
			
		i++;
	}
	
	//Read in Title and Author from file
	for(i = 0; i < 16; i++)
	{
		if(fs->dirContents[i].used)
		{
			/******************************
		 * Tags to read from a file:
		 * 
		 * # - Comment in file
		 * 
		 * ! - Title
		 * @ - Author
		 * 
		 * % - Instrument Change
		 * 
		 ******************************/
			sd_read_single_block(fs->filesystemInfo.firstDataSector + fs->dirContents[i].clusterOffset);
			
			
			//Find Title
			j = 0;
			
			fs->dirContents[i].tagTitle[0] = '\0';
			while( (j < 512) && (recvbuf[j] != '!')) j++;
			
			j++;
			k = 0;
			
			if(j < 512)
			{
				//If found Title tag and not at end of first sector
				while( (k < 50) && (j < 512) && ((recvbuf[j] != '\r') && (recvbuf[j] != '\n'))) fs->dirContents[i].tagTitle[k++] = recvbuf[j++];
				
				if(k >= 50)
					k = 49;
					
				
			}
			
			fs->dirContents[i].tagTitle[k] = 0;
			
			/*if(strlen(fs->dirContents[i].tagTitle) == 0)
				strncpy(fs->dirContents[i].tagTitle, blankName, 7);*/
			
			
			//Find Author
			j = 0;
			
			fs->dirContents[i].tagAuthor[k] = '\0';
			while( (j < 512) && (recvbuf[j] != '@')) j++;
			
			j++;
			k = 0;
			
			if(j < 512)
			{
				//If found Author tag and not at end of first sector
				while( (k < 50) && (j < 512) && ((recvbuf[j] != '\r') && (recvbuf[j] != '\n'))) fs->dirContents[i].tagAuthor[k++] = recvbuf[j++];
				
				if(k >= 50)
					k = 49;
					
				
			}
			
			fs->dirContents[i].tagAuthor[k] = '\0';
			
			/*if(strlen(fs->dirContents[i].tagAuthor) == 0)
				strncpy(fs->dirContents[i].tagAuthor, blankName, 7);*/
		}
	}
	
	return 0;
}

//Context Switch into next directory
int changeContext(MF_FAT32_Filesystem *fs, MF_FileInfo *toRead)
{
	if(!toRead->isFolder)
		return -1;
		
	if(fs->currStackNo <= 0)
	{
		if(fs->currStackNo <= toRead->directoryLevel)
		{
			//IF GOING TO DEEPER SUBFOLDER STOR INFO
			fs->dirStack[fs->currStackNo].clusterOffset = fs->currDirectory.clusterOffset;
			fs->dirStack[fs->currStackNo].directoryLevel = 	fs->currDirectory.directoryLevel;
			strncpy(fs->dirStack[fs->currStackNo].name, fs->currDirectory.name, 50);
			fs->dirStack[fs->currStackNo].parentDir = fs->currDirectory.parentDir;
			fs->dirStack[fs->currStackNo].used = 1;
			fs->dirStack[fs->currStackNo].isFile = 0;
			fs->dirStack[fs->currStackNo].isFolder = 1;
		}
		else
		{
			//GOING UP IN SUBFOLDERS, CLEAR RECENT FOLDER ON STACK
			fs->dirStack[fs->currStackNo].clusterOffset = 0;
			fs->dirStack[fs->currStackNo].directoryLevel = 	0;
			fs->dirStack[fs->currStackNo].name[0] = 0;
			fs->dirStack[fs->currStackNo].parentDir = 0;
			fs->dirStack[fs->currStackNo].used = 0;
			fs->dirStack[fs->currStackNo].isFile = 0;
			fs->dirStack[fs->currStackNo].isFolder = 0;
			
			fs->currStackNo -= 2;
		}
	}
	
	fs->currStackNo++;
	
	fs->currDirectory.clusterOffset = toRead->clusterOffset;
	fs->currDirectory.directoryLevel = toRead->directoryLevel;
	strncpy(fs->currDirectory.name, toRead->name, 50);
	fs->currDirectory.parentDir = toRead->parentDir;
	
	readDirectoryInfo(fs, 0);
	
	return 0;
}

unsigned long findCluster(MF_FAT32_Filesystem *fs, unsigned long clusterNo, int depth)
{
	unsigned long PrevFATCluster;
	unsigned long FATCluster;
	unsigned long FATClusterOffset;
	unsigned long OffsetInFATTable;
	unsigned long ClusterNumber;
	int i;
	
	if(depth < 0)
		return 0xFFFFFFFF;
	
	PrevFATCluster = 0xFFFFFFFF;
	ClusterNumber = clusterNo + fs->filesystemInfo.rootCluster;
	
	for(i = 0; i < depth; i++)
	{
		OffsetInFATTable = ClusterNumber % 64;
		FATClusterOffset = ClusterNumber / 64;
		FATCluster = fs->filesystemInfo.FAT_Begin_Cluster + FATClusterOffset;
	
		if(PrevFATCluster != FATCluster)
			sd_read_single_block(FATCluster);
		
		PrevFATCluster = FATCluster;
		
		//Get Next File Location
		ClusterNumber = combineInformation((unsigned char*)&recvbuf[OffsetInFATTable * 4], 4);
	}
	
	return (ClusterNumber - fs->filesystemInfo.rootCluster);
}

int getSongData(MF_FAT32_Filesystem *fs, unsigned long clusterNo, char *title, int titleLen, char *author, int authLen)
{
	int i = 0;
	int Done = 0;
	int titl = 0;
	int auth = 0;
	int titleLoaded = 0;
	int authorLoaded = 0;
	int titleOffset = 0;
	int authorOffset = 0;
		
	sd_read_single_block(fs->filesystemInfo.firstDataSector + clusterNo);
	
	do
	{
		if(i >= 512)
		{
			Done = 1;
		}
		else
		{
			if(recvbuf[i] == '!')
			{
				if(!titleLoaded)
					titl = 1;
			}
			else if(recvbuf[i] == '@')
			{
				if(!authorLoaded)
					auth = 1;
			}
			else if( (recvbuf[i] == '\n') || (recvbuf[i] == '\r') )
		    {
		    	//End of line, process data if not a special condition
		    	
		    	if(recvbuf[i] == '\r')
		    		i++;
		    
		    	if(titl)
		    		titleLoaded = 1;
		    	
		    	if(auth)
		    		authorLoaded = 1;
		    		
		    }
			else
			{
				if(titl)
		    	{
		    		//If not over the buffer length
		    		if(titleOffset < titleLen)
		    		{
		    			title[titleOffset] = recvbuf[i];
		    			titleOffset++;	
		    		}
		    	}
		    	else if(auth)
		    	{
		    		if(authorOffset < authLen)
		    		{
		    			author[authorOffset] = recvbuf[i];
		    			authorOffset++;	
		    		}
		    	}
			}
		}
			
		i++;
	
	}while(!Done);
	
	return (titleLoaded && authorLoaded);
}

int loadSong(MF_FAT32_Filesystem *fs, unsigned long clusterNo, unsigned long finalBuff[], Uint16 lenOfBuff)//, char *title, int titleLen, char *author, int authLen, int *instrument)
{
	int i = 0;
	int Done = 0;
	int notePointer = 0;
	int depth = 0;
	unsigned long tempCheck = 0;
	unsigned long tempNumber = 0;
	int comment = 0;
	int inst = 0;
	int titl = 0;
	int autho = 0;
	
	sd_read_single_block(fs->filesystemInfo.firstDataSector + clusterNo);
	
	do
	{
		/******************************
		 * Tags to read from a file:
		 * 
		 * # - Comment in file
		 * 
		 * ! - Title
		 * @ - Author
		 * 
		 * % - Instrument Change
		 * 
		 ******************************/
		
		//First Check for end of Sector
		if(i >= 512)
		{
			i = 0;
			depth++;
			sd_read_single_block(fs->filesystemInfo.firstDataSector + findCluster(fs, clusterNo, depth));
		}
		
		if(notePointer >= lenOfBuff)
		{
			Done = 1;
		}
		else if(recvbuf[i] == 0x00)
		{
			//END OF FILE
			Done = 1;
			
			if(tempNumber != 0)
			{
				finalBuff[notePointer] = tempNumber;
		    	notePointer++;
		    	tempNumber = 0;
			}
		}
		else // Handling the data after Checking for EOF and no more room for notes
		{
			//Check for special conditions
			if(recvbuf[i] == '#')
			{
				//Comment line, do nothing till newline
				comment = 1;
			}
			else if(recvbuf[i] == '!')
			{
				//Title Line
				titl = 1;
			}
			else if(recvbuf[i] == '@')
			{
				//Author Line
				autho = 1;
			}
			else if(recvbuf[i] == '%')
			{
				//Instrument Change
				inst = 1;
			}
		    else if( (recvbuf[i] == '\n') || (recvbuf[i] == '\r') )
		    {
		    	//End of line, process data if not a special condition
		    	if(!comment && !titl && !autho)
		    	{
		    		if(inst)
		    		{
		    			;//*instrument = tempNumber;
		    		}
		    		else
		    		{
		    			finalBuff[notePointer] = tempNumber;
		    			notePointer++;
		    		}
		    	}
		    
		    	tempNumber = 0;
		    	
		    	if(recvbuf[i] == '\r')
		    		i++;
		    
		    	comment = 0;
		    	titl = 0;
		    	autho = 0;
		    	inst = 0;
		    		
		    }
		    else //If not a newline or return, process the character
		    {
		    	if(titl)
		    	{
		    		//If not over the buffer length
		    		/*if(titleOffset < titleLen)
		    		{
		    			title[titleOffset] = recvbuf[i];
		    			titleOffset++;	
		    		}*/
		    	}
		    	else if(autho)
		    	{
		    		/*if(authorOffset < authLen)
		    		{
		    			author[authorOffset] = recvbuf[i];
		    			authorOffset++;	
		    		}*/
		    	}
		    	
		    	
		    	//Here we are processing numbers, numbers range from 0 -> 9, discard anything larger than that	
		    	//It is an unsigned long, so doing a subtraction would wrap around any negative numbers (to numbers > 9)
		    	tempCheck = recvbuf[i] - '0';
		    	
		    	if(tempCheck <= 9)
		    	{
		    		tempNumber *= 10;
		    		tempNumber += tempCheck;
		    	}
		    }
		    
		}
		
		i++;
		
	}while(!Done);   
	
	for(i = notePointer; i < lenOfBuff; i++)
		finalBuff[i] = 0;
	
	return notePointer;
}
/*********************************
 *
 *	String to long converter
 *
 *********************************/
unsigned long combineInformation(unsigned char toCombine[], Uint16 len)
{
	int i;
	unsigned long toReturn = 0;
	
	for(i = len - 1; i >= 0; i--)
	{
		toReturn = toReturn << 8;
		toReturn = toReturn | toCombine[i];
	}
	
	return toReturn;
}
